from data import Data
from verifier import Verifier
import os
import time
from random import shuffle
from multiprocessing import Pool
from utils import print_sol_to_std,print_sol_to_file
from tqdm import tqdm


def batch_solve(files, mySolver):
    total, total_time = 0, 0
    for filename in tqdm(files):
        data_path = 'data/' + filename
        print(data_path, 'start running!')
        sol_path = 'best_sol/' + filename.replace('.txt', '.out')
        if not os.path.isfile(sol_path):
            val = 1000000000
        else:
            verifier = Verifier(data_path=data_path)
            verifier.load_sol_from_file(data_path=sol_path)
            verifier.verify(show_info=False)
            val = verifier.get_finished_time()
        verifier = Verifier(data_path=data_path)
        solver = mySolver(data_path=data_path)
        a = time.time()
        sol = solver.solve()
        total_time += time.time() - a
        verifier.load_sol_from_param(sol=sol)
        verifier.verify(show_info=False)
        total += verifier.get_finished_time()
        if verifier.get_finished_time() < val:
            print('update %s from %d to %d' % (data_path.replace('.txt', ''), val, verifier.get_finished_time()))
            print_sol_to_file(sol=sol, file_path=sol_path)
    return [total,total_time]


class Engine:

    def __init__(self, mySolver):
        self.mySolver = mySolver
    
    def print_sol_to_std(self, sol):
        print_sol_to_std(sol)

    def print_sol_to_file(self, sol, file_path='data.out'):
        print_sol_to_file(sol, file_path)

    def solve_from_file_to_std(self, data_path='data.txt'):
        solver = self.mySolver(data_path=data_path)
        verifier = Verifier(data_path=data_path)
        a = time.time()
        sol = solver.solve()
        solve_time = time.time() - a
        verifier.load_sol_from_param(sol)
        self.print_sol_to_std(sol)
        verifier.score()
        print('运行时间: %.2fs' % solve_time)
        val2 = verifier.get_finished_time()
        verifier = Verifier()
        verifier.load_sol_from_file()
        verifier.verify(show_info=False)
        val = verifier.get_finished_time()
        print('官方数据集已知最优解:', val)
        if val > val2:
            self.print_sol_to_file(sol=sol)

    def solve_from_file_to_file(self, data_path='data.txt', output_path='data.out'):
        solver = self.mySolver(data_path=data_path)
        verifier = Verifier(data_path=data_path)
        sol = solver.solve()
        verifier.load_sol_from_param(sol)
        verifier.verify(show_info=False)
        self.print_sol_to_file(sol, file_path=output_path)

    def batch_update_best_sol_for_all_data_by_solve(self):
        f = open('data_list.txt', 'r', encoding='utf-8')
        total, total_time = 0, 0
        new_line = []
        pros = []
        cpu_c = 6
        lines = f.readlines()
        shuffle(lines)
        blocks = len(lines) // cpu_c
        p = Pool(cpu_c)
        for line in lines:
            line = line.strip()
            new_line.append(line)
            if len(new_line) == blocks:
                pros.append(p.apply_async(batch_solve, args=(new_line, self.mySolver)))
                new_line = []
        if len(new_line)>0:
            pros.append(p.apply_async(batch_solve, args=(new_line, self.mySolver)))
            new_line = []
        p.close()
        p.join()
        for pro in pros:
            res = pro.get()
            total += res[0]
            total_time += res[1]
        print('你的算法在其他数据集的时间之和:', total)
        print('运行时间: %.2fs' % total_time)    

    def update_best_sol_for_all_data_by_solve(self):
        f = open('data_list.txt', 'r', encoding='utf-8')
        total = 0
        total_time = 0
        for line in f.readlines():
            data_path = 'data/' + line.strip()
            print(data_path, 'start running!')
            sol_path = 'best_sol/' + line.strip().replace('.txt', '.out')
            if not os.path.isfile(sol_path):
                val = 1000000000
            else:
                verifier = Verifier(data_path=data_path)
                verifier.load_sol_from_file(data_path=sol_path)
                verifier.verify(show_info=False)
                val = verifier.get_finished_time()
            verifier = Verifier(data_path=data_path)
            solver = self.mySolver(data_path=data_path)
            a = time.time()
            sol = solver.solve()
            total_time += time.time() - a
            verifier.load_sol_from_param(sol=sol)
            verifier.verify(show_info=False)
            total += verifier.get_finished_time()
            if verifier.get_finished_time() < val:
                print('update %s from %d to %d' % (data_path.replace('.txt', ''), val, verifier.get_finished_time()))
                self.print_sol_to_file(sol=sol, file_path=sol_path)
        print()
        print('你的算法在其他数据集的时间之和:', total)
        print('运行时间: %.2fs' % total_time)

    def list_best_solution_value_for_all_data(self, list_every_data=False):
        f = open('data_list.txt', 'r', encoding='utf-8')
        total = 0
        for line in f.readlines():
            data_path = 'data/' + line.strip()
            sol_path = 'best_sol/' + line.strip().replace('.txt', '.out')
            if not os.path.isfile(sol_path):
                val = 1000000000
            else:
                verifier = Verifier(data_path=data_path)
                verifier.load_sol_from_file(data_path=sol_path)
                verifier.verify(show_info=False)
                val = verifier.get_finished_time()
            if list_every_data:
                print(line.strip().replace('.txt', '') + ':', val)
            total += val
        print('其他数据集已知最优解时间之和:', total)

    def analyse_all_data(self):
        f = open('data_list.txt', 'r', encoding='utf-8')
        max_n, max_m, sum_n, sum_m, cnt, min_n, min_m, max_k, min_k, sum_k = 0, 0, 0, 0, 0, 10000, 10000, 0, 10000, 0
        for line in f.readlines():
            data = Data(data_path='data/' + line.strip())
            max_n = max(max_n, data.n)
            max_m = max(max_m, data.m)
            max_k = max(max_k, data.avg_available_machine_num)
            sum_n += data.n
            sum_m += data.m
            sum_k += data.avg_available_machine_num
            min_n = min(min_n, data.n)
            min_m = min(min_m, data.m)
            min_k = min(min_k, data.avg_available_machine_num)
            cnt += 1
        print('n:  max -', max_n, 'min -', min_n, 'avg -', format(sum_n / cnt, '.2f'))
        print('m:  max -', max_m, 'min -', min_m, 'avg -', format(sum_m / cnt, '.2f'))
        print('avg_available_machine_num:  max -', max_k, 'min -', min_k, 'avg -', format(sum_k / cnt, '.2f'))
